package io.lkt.task.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.*;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RedisCacheUtil<T> {

    @Autowired
    @Qualifier("redisTemplate")
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象，Integer、String、实体类等
     * @param key 缓存的键值
     * @param value 缓存的值
     * @return 缓存的对象
     */
    public <T> ValueOperations<String,T> setCacheObject(String key, T value, long time) {

        log.info("[setObject-key] = " + key + " [value] = " + value);

        ValueOperations<String,T> operation = redisTemplate.opsForValue();

        operation.set(key, value, time, TimeUnit.SECONDS);

        return operation;

    }

    /**
     * 获得缓存的基本对象。
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(String key) {

        //log.info("[getObject-key] = " + key);

        ValueOperations<String,T> operation = redisTemplate.opsForValue();

        T value = operation.get(key);

        //log.info("[getObject-key] = " + key + " [getObject-value] = " + value);
        return value;

    }

    /**

     * 缓存List数据

     * @param key 缓存的键值

     * @param dataList 待缓存的List数据

     * @return 缓存的对象

     */

    public <T> ListOperations<String, T> setCacheList(String key, List<T> dataList) {

        ListOperations listOperation = redisTemplate.opsForList();

        if(null != dataList) {

            int size = dataList.size();

            for(int i = 0; i < size ; i ++) {
                listOperation.rightPush(key,dataList.get(i));
            }

        }

        return listOperation;

    }

    /**

     * 获得缓存的list对象

     * @param key 缓存的键值

     * @return 缓存键值对应的数据

     */

    public <T> List<T> getCacheList(String key)

    {

        List<T> dataList = new ArrayList<T>();

        ListOperations<String,T> listOperation = redisTemplate.opsForList();

        Long size = listOperation.size(key);

        for(int i = 0 ; i < size ; i ++) {

            dataList.add((T) listOperation.leftPop(key));

        }

        return dataList;

    }

    /**
     * 缓存Set
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String,T> setCacheSet(String key, Set<T> dataSet) {

        BoundSetOperations<String,T> setOperation = redisTemplate.boundSetOps(key);

        Iterator<T> it = dataSet.iterator();

        while(it.hasNext()) {

            setOperation.add(it.next());

        }

        return setOperation;
    }

    /**
     * 获得缓存的set
     * @param key
     * @return
     */
    public Set<T> getCacheSet(String key) {

        Set<T> dataSet = new HashSet<T>();

        BoundSetOperations<String,T> operation = redisTemplate.boundSetOps(key);

        Long size = operation.size();

        for(int i = 0 ; i < size ; i++) {

            dataSet.add(operation.pop());

        }

        return dataSet;

    }

    /**
     * 缓存Map
     * @param key
     * @param dataMap
     * @return
     */
    public <T> HashOperations<String,String,T> setCacheMap(String key, Map<String,T> dataMap) {

        HashOperations hashOperations = redisTemplate.opsForHash();

        if(null != dataMap) {

            for (Map.Entry<String, T> entry : dataMap.entrySet()) {

                hashOperations.put(key,entry.getKey(),entry.getValue());

            }
        }

        return hashOperations;

    }

    /**
     * 获得缓存的Map
     * @param key
     * @return
     */
    public <T> Map<String,T> getCacheMap(String key) {

        Map<String, T> map = redisTemplate.opsForHash().entries(key);

        return map;

    }

    /**
     * 缓存Map
     * @param key
     * @param dataMap
     * @return
     */
    public <T> HashOperations<String,Integer,T> setCacheIntegerMap(String key, Map<Integer,T> dataMap) {
        HashOperations hashOperations = redisTemplate.opsForHash();
        if(null != dataMap)
        {
            for (Map.Entry<Integer, T> entry : dataMap.entrySet()) {

                hashOperations.put(key,entry.getKey(),entry.getValue());

            }
        }
        return hashOperations;
    }
    /**
     * 获得缓存的Map
     * @param key
     * @return
     */
    public <T> Map<Integer,T> getCacheIntegerMap(String key) {

        Map<Integer, T> map = redisTemplate.opsForHash().entries(key);

        return map;

    }

    /**
     * 移除缓存
     * @param key
     * @return
     */
    public boolean removeSingleCache(String key) {
        try {
            redisTemplate.delete(key);
            return true;
        } catch (Throwable t) {
            log.error("获取缓存失败key[" + key + ", error[" + t + "]");
        }

        return false;
    }

    /**
     * 随机获得缓存的set
     * @param key
     * @return
     */
    public Set<T> getCacheSetForRandomEle(String key, long count) {

        log.info("[getCacheSetForRandomEle] = " + key);
        Set<T> dataSet = redisTemplate.opsForSet().distinctRandomMembers(key, count);

        return dataSet;
    }

    /**
     * 往Set中添加元素
     * @param key
     * @param value
     * @return
     */
    public Long addEleToSet(String key, T value) {
        return redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 删除set中的元素
     * @param key
     * @param o
     * @return
     */
    public boolean removeEleInSet(String key, Object o) {
        Long result = redisTemplate.opsForSet().remove(key, o);
        checkResult(result);
        return result == 1;
    }

    protected void checkResult(@Nullable Object obj) {

        if (obj == null) {
            throw new IllegalStateException("Cannot read collection with Redis connection in pipeline/multi-exec mode");
        }
    }

    /**
     * 模糊查询获得缓存的set
     * @param key
     * @return
     */
    public Set<T> getPatternCacheSet(String key) {

        log.info("[getPatternCacheSet-key] = " + key);

        Set<T> dataSet = redisTemplate.keys(key + "*");

        log.info("[getPatternCacheSet-key] = " + key + " [getObject-value] = " + dataSet.size());

        return dataSet;

    }

    /**
     * 模糊匹配获得缓存set
     * @param pattern
     */
    public Set<Object> getObjectByScanPattern(String pattern) {

        Set<Object> dataSet = (Set<Object>) redisTemplate.execute(new RedisCallback<Set<Object>>() {

            @Override
            public Set<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                Set<Object> binaryKeys = new HashSet<>();

                ScanOptions scanOptions = new ScanOptions.ScanOptionsBuilder().match(pattern).count(Integer.MAX_VALUE).build();

                Cursor<byte[]> cursor = connection.scan(scanOptions);

                while (cursor.hasNext()) {

                    binaryKeys.add(new String(cursor.next()));

                }

                return binaryKeys;
            }
        });

        log.info("[scanKeysSize] = " + dataSet.size());

        return dataSet;

    }

}
